#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "symbol.h"
#include "libblogc/list.h"
#include "node.h"
#include "generate.h"
#include "global.h"
#include "semantic.h"

extern void yyrestart( FILE *new_file );
extern int yyparse(void);
extern int yylineno;
extern list_t prs_obs_list;
extern list_t prs_qry_list;

char * yyfilename;

global_t * global;

/*
  strips the suffix and allocates plenty of extra space
 */
static char * strip_suffix(char * fname)
{
  char * out;
  int i;
  
  out = (char *)malloc(2000);
  strcpy(out, fname);
  
  for (i = strlen(out)-1; i>=0; i--)
    if (out[i] == '.')
    {
      out[i] = '\0';
      break;
    }
  
  return out;
}

/* returns the filename without the directory */
static char * strip_directory(char * fname)
{
  char * out;
  int i;
  
  for (i = strlen(fname)-1; i>=0; i--)
    if (('\\' == fname[i]) || ('/' == fname[i]))
      break;
  
  out = (char *)malloc(strlen(fname)+1);
  
  strcpy(out, fname+i+1);
  return out;
}

static void print_usage(void)
{
  printf("Usage: blogc [options] blog-files\n");
  printf("Options:\n");
  printf("-v  verbose\n");
  printf("-opt:no_block to disable block sampling\n");
  printf("-opt:no_gibbs to disable Gibbs sampling\n");
  printf("-opt:num2real to report natural numbers as real\n");
}

static void init_globals(void)
{
  symbol_table_alloc();                    /* allocate symbol table */
  prs_obs_list = list_alloc();
  prs_qry_list = list_alloc();

  /* initialize options */
  global = (global_t *) calloc(1, sizeof(*global));
  global->distlist = list_alloc();
}

int main(int argc, char ** argv)
{
  void * iter;
  char * outname;                        /* name of the output file */
  FILE * fp_out;

  init_globals();

  printf(PACKAGE " " VERSION " -- BLOG Compiler \n");
  
  ++argv, --argc;                         /* skip over program name */
  outname = NULL;  
  /* parse all the files on the command line */
  while(argc > 0)
  {
    FILE * fp;

    /* first check if this is an option */
    if (!strcmp(*argv, "-h"))
    {
      print_usage();
      exit(0);
    }

    if (!strcmp(*argv, "-v"))
    {
      global->verbose = 1;
      argv ++;
      argc --;
      continue;
    }

    if (!strcmp(*argv, "-opt:no_block"))
    {
      global->no_block = 1;
      argv ++;
      argc --;
      continue;
    }

    if (!strcmp(*argv, "-opt:no_gibbs"))
    {
      global->no_gibbs = 1;
      argv ++;
      argc --;
      continue;
    }

    if (!strcmp(*argv, "-opt:num2real"))
    {
      global->num2real = 1;
      argv ++;
      argc --;
      continue;
    }

    /* if this is the first file configure the outname */
    if (!outname)
    {
      char * basename;
      
      basename = strip_directory(argv[0]);
      
      outname = strip_suffix(basename);
      if (strlen(outname) >= strlen(basename))
      {
        fprintf(stderr, "No suffix in file %s\n", basename);
        return 1;
      }
      strcat(outname, ".c");
      if (!strcmp(outname, basename))
      {
        fprintf(stderr, "Invalid suffix in file %s\n", basename);
        return 2;
      }
    }

    /* open the next file */
    fp = fopen( argv[0], "r" );
    if (!fp)
    {
      fprintf(stderr, "Unable to open input file: %s\n", argv[0]);
      return 3;
    }

    /* initialize the scanner */
    yyfilename = argv[0];
    yylineno = 1;
    yyrestart(fp);

    /* parse the file */
    if (0 != yyparse())
      return 4;
    else
      printf("Parsed Input file %s successfully\n", argv[0]);
    
    fclose(fp);                              /* close current file */
    ++argv, --argc;                         /* skip to next file */
  }

  if (!outname)
  {
    fprintf(stderr, "Error: No blog file specified\n");
    return 5;
  }

  /* now perform semantic parsing of all the symbols in symbol table */
  semantic_parse_second(prs_obs_list, prs_qry_list);
  
  if (global->verbose)
  {
    symbol_table_print();
  
    /* print observations */
    printf("Observations:\n");
    for(iter = list_next(prs_obs_list); NULL != iter;
        iter = list_next(iter))
    {
      node_print((node *)list_item(iter));
      printf("\n");
    }
  
    /* print queries */
    printf("Queries:\n");
    for(iter = list_next(prs_qry_list); NULL != iter;
        iter = list_next(iter))
    {
      node_print((node *)list_item(iter));
      printf("\n");
    }
  }

  fp_out = fopen(outname, "w");
  if (!fp_out)
  {
    fprintf(stderr, "Can't write to file %s\n", outname);
    return 6;
  }

  /* in verbose mode disable buffering, this helps during debugging when
   * we want to see where the program crashed when generating the output */
  if (global->verbose)
  {
    setvbuf(fp_out, NULL, _IONBF, 0);
  }
  
  generate(prs_obs_list, prs_qry_list, fp_out);

  fclose(fp_out);
  
  return 0;
}
